/*
	Copyright (C) 2011 Salil Bhagurkar

	This file is part of illusion

	illusion is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as
	published by the Free Software Foundation, either version 3 of
	the License, or (at your option) any later version.

	illusion is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with illusion. 
	If not, see <http://www.gnu.org/licenses/>.
*/

#include <klib/lib.h>
#include <kernel/storage.h>
#include <kernel/list.h>
#include <kernel/vfs.h>
#include <kernel/errors.h>
#include <kernel/console.h>

static struct storage_dev *storage_devs, *storage_devs_tail;

/*
 * Node syncing is not required on a storage device
 */
static int storage_sync_node(struct vfs_node *this)
{
	return 0;
}

static int storage_sync_page(struct vfs_node *this, struct vfs_page *page)
{
	struct vfs_device *dev_fs = this->dev;
	struct storage_dev *dev = (struct storage_dev *)dev_fs->class;
	if(page->flags & VFS_SYNC_DEV) {
		return dev->read(page->page_data, page->start, page->length);
	} else if(page->flags & VFS_SYNC_MEM) {
		return dev->write(page->page_data, page->start, page->length);
	}
	return ESTINV;
}

/*
 * Tree traversal, too, is not required for a storage device
 */
static struct vfs_node *storage_get_child(struct vfs_node *parent)
{
	return null;
}

static struct vfs_node *storage_next_child(struct vfs_node *child)
{
	return null;
}

/*
 * The sync provider for all storage devices in the system
 */
static struct vfs_sync_provider storage_sync = {
	.name = "storage_sync",
	.flags = 0,
	.supported_page_streams = "data",
	.sync_node = storage_sync_node,
	.sync_page = storage_sync_page,
	.get_child = storage_get_child,
	.next_child = storage_next_child,
};

void register_storage_dev(struct storage_dev *dev)
{
	list_attach(storage_devs, storage_devs_tail, dev);
	struct vfs_device *dev_fs = vfs_create_device(dev->name, dev->block_size);
	vfs_set_sync_provider(dev_fs, &storage_sync);
	dev_fs->root->length = dev->total_blocks;
	dev_fs->class = dev;
	dev->dev_fs = dev_fs;
}

void unregister_storage_dev(struct storage_dev *dev)
{
	list_detach(storage_devs, storage_devs_tail, dev);
	struct vfs_device *dev_fs = dev->dev_fs;
	vfs_remove_sync_provider(dev_fs);
	vfs_remove_device(dev->name, false);
}
